﻿using ASmile.Extend;
using ASmile.Win;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace ASmile.Objects
{
    /// <summary>
    /// 自定义对象加载器(仿spring.net)
    /// </summary>
    public class ObjectLoader
    {
        static Dictionary<string, Assembly> AssemblyList { get; } = new Dictionary<string, Assembly>();

        static Dictionary<string, ConfObject> ResourceObjects { get; } = new Dictionary<string, ConfObject>();

        static Dictionary<string, object> SingletonObjects { get; } = new Dictionary<string, object>();

        public static void LoadConf()
        {
            var resourceType = ConfigHelper.GetAppSettings("ResourceType");
            if (resourceType.IsEmpty())
            {
                resourceType = "Assembly";
            }
            var resourceValue = ConfigHelper.GetAppSettings("DefObjectResource");
            if (resourceValue.IsEmpty())
            {
                var asm = Assembly.GetEntryAssembly();
                var typs = asm.GetTypes();
                var nameSpace = typs.First(s=>s.Name.StringEquals("Program")).Namespace;
                resourceValue = $"{asm.GetName().Name},{nameSpace}.Conf.Root.json";
            }
            LoadConf(resourceType, resourceValue);
        }

        public static void LoadConf(string resourceType, string resourceValue)
        {
            try
            {
                ConfResource conf = new ConfResource
                {
                    ResourceType = resourceType,
                    ResourceValue = resourceValue
                };
                LoadConf(conf);

            }
            catch (Exception ex)
            {
                LogHelper.LogObj.Error("载入配置发生异常", ex);
                System.Windows.Forms.MessageBox.Show("载入配置时发生异常");
            }
        }

        static void LoadConf(ConfResource defResource)
        {
            var resType = defResource.ResourceType;
            if (resType.IsEmpty() || resType.StringEquals("Assembly"))
            {
                var vals = defResource.ResourceValue.SplitByNotEmpty(",");
                var asmName = vals[0];
                var resPath = vals[1];
                Assembly asm;
                if (AssemblyList.ContainsKey(asmName))
                {
                    asm = AssemblyList[asmName];
                }
                else
                {
                    asm = Assembly.Load(asmName);
                }
                string confJson;
                using (var sr = asm.GetManifestResourceStream(resPath))
                {
                    if (sr == null)
                    {
                        LogHelper.LogObj.Error($"程序集资源：{resPath} 为空");
                        return;
                    }
                    using (var ss = new StreamReader(sr))
                    {
                        confJson = ss.ReadToEnd();
                        ConfFileHandle(confJson);
                    }
                }
            }
            else if (resType.StringEquals("File"))
            {
                string rootPath = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
                var confFile = Path.Combine(rootPath, defResource.ResourceValue);
                var confJson = File.ReadAllText(confFile);
                ConfFileHandle(confJson);
            }
        }
        /// <summary>
        /// 配置文件处理
        /// </summary>
        static void ConfFileHandle(string jsonFile)
        {
            Conf confObj = JsonHelper.JsonToObject<Conf>(jsonFile);

            if (confObj.Resources != null)
            {
                foreach (var item in confObj.Resources)
                {
                    LoadConf(item);
                }
            }
            if (confObj.Objects != null)
            {
                foreach (var item in confObj.Objects)
                {
                    if (ResourceObjects.ContainsKey(item.Name))
                    {
                        LogHelper.LogObj.Error($"服务名：{item.Name} 重复加载");
                        continue;
                    }
                    if (item.AssemblyName.IsEmpty())
                    {
                        item.AssemblyName = confObj.DefAssembly;
                    }
                    if (item.Singleton == null)
                    {
                        item.Singleton = confObj.DefSingleton;
                    }
                    if (item.LazyInit == null)
                    {
                        item.LazyInit = confObj.DefSingleton;
                    }
                    if (!AssemblyList.TryGetValue(item.AssemblyName, out Assembly asm))
                    {
                        asm = Assembly.Load(item.AssemblyName);
                        AssemblyList[item.AssemblyName] = asm;
                    }
                    if (item.LazyInit == true)
                    {
                        item.Instance = CreateObject(item, asm);
                    }
                    ResourceObjects[item.Name] = item;
                }
            }
        }

        static object CreateObject(ConfObject conf, Assembly asm = null)
        {
            if (asm == null)
            {
                asm = AssemblyList[conf.AssemblyName];
            }
            var type = asm.GetType(conf.TypeName, true, true);
            var obj = Activator.CreateInstance(type);
            if (conf.PropertyDefs?.Length > 0)
            {
                SetProperty(obj, conf.PropertyDefs);
            }
            return obj;
        }

        static void SetProperty(object obj, params ConfObjectProperty[] propertys)
        {
            if (obj == null || propertys == null) return;

            foreach (var item in propertys)
            {
                if (item.PropertyName.IsEmpty()) continue;
                Type t = obj.GetType();
                var pro = t.GetProperty(item.PropertyName,BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                if (pro == null) continue;

                if (pro.PropertyType.IsClass && pro.PropertyType != typeof(string))
                {
                    if (item.SubProperty == null) continue;
                    var subObj = pro.GetValue(obj, null);
                    SetProperty(subObj, item);
                }
                else
                {
                    object pVal;
                    if (pro.PropertyType != typeof(string))
                    {
                        pVal = Convert.ChangeType(item.PropertyValue, pro.PropertyType);
                    }
                    else
                    {
                        pVal = item.PropertyValue;
                    }
                    pro.SetValue(obj, pVal, null);
                }
            }
        }

        public static object GetObject(string name)
        {
            if (!ResourceObjects.TryGetValue(name, out ConfObject confObj))
            {
                return null;
            }
            if (confObj.Singleton == true)
            {
                if (!SingletonObjects.TryGetValue(name, out object obj))
                {
                    obj = CreateObject(confObj);
                }
                return obj;
            }
            return CreateObject(confObj);
        }

        public static T GetObject<T>(string name)
            where T : class
        {
            var r = GetObject(name) as T;
            return r;
        }
    }
}